#pragma once
#include<iostream>
#include<string>
#include<time.h>
#include<stdarg.h>
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include"Lockguard.hpp"

#include<fstream> 


pthread_mutex_t _log_mutex = PTHREAD_MUTEX_INITIALIZER;

bool IsSave = false;
const std::string filename = "./log.txt";

/*a.在宏定义中,包含__FILE__、__LINE__,就不需要每次调用宏函数时手动传递它们,宏会自动处理;
b.在C99、C++11中宏支持可变参数(...);
c.使用__VA_ARGS__来在宏展开时替换为实际传递的参数列表,
##主要用途是处理可变参数个数为0(为空、不存在),以避免在展开时产生额外的逗号或标记;
d.do{...}while(0)
    1.即使宏被用在更复杂的语句结构中,如作为if语句的一部分,它能够保证宏展开为一个单独的代码块,
这样可以避免由于宏展开的代码和周围的代码混合而导致的问题
    2.防止宏在使用由于误加逗号而导致的编译错误*/
#define LOG(level, format, ...) do{ LogMessage(__FILE__, __LINE__, IsSave, level, format, ##__VA_ARGS__); }while(0)

#define EnableFile() do{ IsSave = true; }while(0)
#define EnableScreen() do{ IsSave = false; }while(0)

/*日志是有等级的
定义一个枚举类型,它包含一组整形常量,用于表示日志的等级,用于区分日志消息的严重程度*/
enum Level
{
    Debug, //调试级别,记录详细的调试信息,帮助理解程序的运行状态和流程
    Info,  //信息级别,记录程序运行时的一般信息,如:程序的启动、关闭、一般操作等
    Warning, //警告级别,表示出现了一些不影响程序的运行但需要注意的问题
    Error, //错误级别,程序运行过程中遇到了错误,这些错误并未导致程序崩溃或停止运行
    Fatal //致命级别,程序遇到了严重错误,无法运行,通常会导致程序崩溃
};

//将日志等级转化为字符串
std::string LevelString(int level) 
{
    switch(level)
    {
        case Debug:
            return "Debug";
        case Info:
            return "Info";
        case Warning:
            return "Warning";
        case Error:
            return "Error";
        case Fatal:
            return "Fatal";
        default:
            return "Unkown";
    }
}

//将当前时间转化为字符串
std::string Time()
{
    char ret[4096];
    time_t curtime = time(nullptr); //获取当前时间的时间戳(当前与1970.1.1的秒数)
    struct tm* t = localtime(&curtime); //将时间戳转化为本地时间(考虑时区的时间)
    snprintf(ret, 4096, "%d-%d-%d %d:%d:%d", t->tm_year+1900, t->tm_mon+1, t->tm_mday, 
    t->tm_hour, t->tm_min, t->tm_sec);
    return ret;
}

//将日志消息保存在文件中
void SaveFile(std::string message)
{
    /*ofstream、fstream都是C++标准库中的类,用于文件的输入输出(I/O)操作、头文件都是<fstream>
    ofstream是一个输出文件流类,用于将数据写入文件中、
    fstream是一个文件流类,用于文件的读写操作,它继承了ofstream类的特性,因此既可以读取文件、又可以向文件写入*/
    std::ofstream out(filename, std::ios::app);
    if(!out.is_open())
    {
        std::cout << "Error open file!" << std::endl; 
    }
    out << message; //运算符<<,与cout对象一样,将数据写入到文件中
}

//日志是有格式的
//日志等级 时间 代码所在函数的名字或行数 日志的内容
void LogMessage(std::string filename, int line, bool issave, int level, const char* format, ...)
{
    std::string levelstr = LevelString(level);
    std::string time = Time();
    std::string id = std::to_string(getpid());

    char buffer[4096];
    va_list ap;
    va_start(ap, format);
    vsnprintf(buffer, 4096, format, ap);
    va_end(ap);
    

    std::string message = "[" + time + "]" + "[" + levelstr + "]"
    + "[" + id + "]" + "[" + filename + "]" + 
    "[" + std::to_string(line) + "]"  + buffer  + "\n";

    //RAII风格类  
    LockGuard lg(&_log_mutex); //因为显示器是共享资源,需要加锁保护,防止出现数据不一致

    if(issave) //保存到文件中
        SaveFile(message);
    else //直接向显示器打印
        std::cout << message.c_str() << std::endl;
}   
